package com.base.cn.platform.os.common.utils.pay;

import com.base.cn.platform.os.common.md5.MD5;
import com.base.cn.platform.os.common.utils.StringUtils;
import com.base.cn.platform.os.common.utils.order.GenerateOrderNum;
import com.base.cn.platform.os.common.utils.result.ResultUtil;
import com.base.cn.platform.os.common.utils.web.DeviceUtils;
import com.base.cn.platform.os.common.utils.web.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.*;

/**
 * 微信支付工具
 *
 * @author s.li
 * @create 2018-09-12-9:51
 */
@Component
@Slf4j
public class WeiXinUtils extends PayService{
    /**微信支付路径*/
    private String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    @Override
    public Map<String,Object> goToPay(HttpServletRequest request,
                                      Map<String, String> payConfigMap,
                                      Map<String, String> params,
                                      String clientType) throws Exception {
        //金额
        String totalAmount = params.get("totalAmount");
        //支付成功回调路径
        String notifyUrl = params.get("notifyUrl");
        //商品描述
        String describe = params.get("describe");
        //微信的openId
        String openId = payConfigMap.get("openid");
        Map<String,Object> returnMap = ResultUtil.ERROR();
        //获取微信支付数据包
        TreeMap<String,String> pramsMap = this.createPackage(request,totalAmount,notifyUrl,describe,clientType,openId,payConfigMap);
        //把商品数据包转成XML格式的商品数据包
        String xmlStr = this.generateXmlPackage(pramsMap);
        //发送商品数据包请求，并得到返回结果
        String resultXmlStr = this.doPostXml(url,xmlStr);
        //把从微信得到xml数据进行解析成Map
        Map<String,String> resultMap = this.parseStringXml(resultXmlStr);
        log.info("----------------resultMap:"+resultMap.toString());
        // 通信标识返回状态码
        String returnCode = resultMap.get("return_code");
        // 返回状态消息
        String returnMsg = resultMap.get("return_msg");
        //成功
        if (returnCode.equalsIgnoreCase("SUCCESS")){
            // 业务结果
            String result_code = resultMap.get("result_code");
            if(result_code.equalsIgnoreCase("SUCCESS")) {
                String tradeType = resultMap.get("trade_type");
                if(tradeType.equals("NATIVE")){//PC扫码支付
                    //二维码链接
                    String code_url = resultMap.get("code_url");
                    ResultUtil.setSuccess(returnMap,true);
                    ResultUtil.setEntity(returnMap,code_url);
                }else if(tradeType.equals("JSAPI")){////微信内置支付
                    ResultUtil.setSuccess(returnMap,true);
                    TreeMap<String,String> signParams = new TreeMap<>();
                    signParams.put("timeStamp", String.valueOf(new Date().getTime()/1000L));// 时间戳
                    signParams.put("nonceStr", StringUtils.getRandomString(32));// 随机字符串
                    signParams.put("signType", "MD5");// 微信签名方式
                    signParams.put("package",  "prepay_id="+resultMap.get("prepay_id"));// 预支付订单id
                    if(!clientType.equals(PayFormClient.APPLY)){//非小程序支付
                        //公众号appid
                        signParams.put("appId", payConfigMap.get("appid"));//公众号appId
                    }else{//小程序支付
                        signParams.put("appId",payConfigMap.get("applyAppId"));//小程序appid
                    }
                    String sign = this.createSign(signParams,payConfigMap.get("payKey"));
                    signParams.put("sign",sign);
                    ResultUtil.setEntity(returnMap,signParams);
                }else if(tradeType.equals("MWEB")){//H5支付
                    ResultUtil.setSuccess(returnMap,true);
                    //唤醒手机端微信应用的链接
                    ResultUtil.setEntity(returnMap,resultMap.get("mweb_url"));
                }else if(tradeType.equals("APP")){//APP支付
                    TreeMap<String,String> signParams = new TreeMap<>();
                    signParams.put("appid",resultMap.get("appid"));//公众号appid
                    signParams.put("partnerid",resultMap.get("mch_id"));//公众号商户ID
                    signParams.put("prepayid",resultMap.get("prepay_id"));//预支付订单ID
                    signParams.put("package","Sign=WXPay");//扩展字段 暂填写固定值Sign=WXPay
                    signParams.put("noncestr",StringUtils.getRandomString(32));//随机字符
                    signParams.put("timestamp",String.valueOf(new Date().getTime()/1000L));//时间戳
                    //生成签名
                    String sign = this.createSign(signParams,payConfigMap.get("openPayKey"));
                    signParams.put("sign",sign);//签名
                    ResultUtil.setEntity(returnMap,signParams);
                    ResultUtil.setSuccess(returnMap,true);
                }
                if(ResultUtil.getSuccess(returnMap)){
                    ResultUtil.setMessage(returnMap,"操作成功");
                }
                return returnMap;
            }else{//失败
                String err_code_des = resultMap.get("err_code_des");
                ResultUtil.setMessage(returnMap,err_code_des);
                return returnMap;
            }
        }else{//失败
            ResultUtil.setMessage(returnMap,returnMsg);
            return returnMap;
        }
    }

    @Override
    public Map<String, String> payCallback(HttpServletRequest request,
                                           HttpServletResponse response,
                                           Map<String, String> payConfigMap,
                                           String clientType) throws Exception {
        Map<String,String> resultMap = new HashMap<>();
        try{
            resultMap.put("success","false");
            resultMap.put("message","微信支付回调错误");
            // xml请求解析
            TreeMap<String, String> requestMap = this.parseXml(request);
            String payKey;
            if(clientType.equals(PayFormClient.ANDROID) || clientType.equals(PayFormClient.IOS) ){
                payKey= payConfigMap.get("openPayKey");
            }else{
                payKey= payConfigMap.get("payKey");
            }
            // 验证签名，参数签名及xml签名、通信成功
            if (this.checkSignature(requestMap, payKey) && requestMap.get("return_code").equals("SUCCESS")) {
                if (requestMap.get("result_code").equals("SUCCESS")) {
                    /* 可以在不同状态下获取订单信息，操作商户数据库使数据同步 */
                    // 订单编号,非requestId，给微信的payId
                    String out_trade_no = requestMap.get("out_trade_no");
                    log.info("++++ out_trade_no:" + out_trade_no);
                    // 微信交易号
                    String transaction_id = requestMap.get("transaction_id");
                    log.info("++++ transaction_id:" + transaction_id);
                    // 总价 微信是分为单位，需要转化为元
                    String total_fee = requestMap.get("total_fee");
                    total_fee = String.valueOf(Double.parseDouble(total_fee)/100D);
                    // 附加数据 用户id，订单requestId,订单类型
                    String attach = requestMap.get("attach");
                    log.info("-------------------attach:"+attach);
                    // 校验好状态,在这里可以写入订单的数据处理,
                    this.sendXmlMessage(request, response, "SUCCESS");
                    resultMap.put("success","true");
                    resultMap.put("treadNo",transaction_id);
                    resultMap.put("amount",total_fee);
                    if(StringUtils.isNotEmpty(attach)){
                        String[] arr = attach.split(",");
                        if(arr.length>2){
                            String shopPageUrl = arr[2];
                            resultMap.put("shopPageUrl",shopPageUrl);
                        }
                    }
                    resultMap.put("message","微信支付回调成功");
                }else{
                    log.info("wxpaynotify fail result_code error");
                    log.info("err_code:" + requestMap.get("return_code"));
                    log.info("err_code_des:" + requestMap.get("err_code_des"));
                    log.info("--------------微信支付回调验证失败");
                    this.sendXmlMessage(request, response, "FAIL");
                }
            }else{
                log.info("--------------微信支付回调验证失败");
                this.sendXmlMessage(request, response, "FAIL");
            }
        }catch (Exception e){
            log.error("微信支付回调错误",e);
        }
        return resultMap;
    }

    @Override
    public Map<String, Object> payTransfer(HttpServletRequest request,
                                           TreeMap<String, String> payConfigMap,
                                           Map<String, String> params) throws Exception{
        TreeMap<String,String> pramsMap = createTransferParams(request,payConfigMap,params);
        String xmlStr = generateXmlPackage(pramsMap);
        log.info("==============xmlStr:"+xmlStr);
        String resultXmlStr = sslCart("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers",xmlStr,payConfigMap.get("mch_id"));
        log.info("==============resultXmlStr:"+resultXmlStr);
        Map<String,String> resultMap = this.parseStringXml(resultXmlStr);
        // 通信标识返回状态码
        String returnCode = resultMap.get("return_code");
        // 返回状态消息
        String returnMsg = resultMap.get("return_msg");
        if (returnCode.equalsIgnoreCase("SUCCESS")){//转账成功
            if(resultMap.get("result_code").equals("SUCCESS")){//业务结果
                String paymentNo = resultMap.get("payment_no");//微信付款单号
                Map<String,Object> _resultMap = ResultUtil.SUCCESS();
                _resultMap.put("paymentNo",paymentNo);
                return _resultMap;
            }else{
                log.info("============err_code:"+resultMap.get("err_code"));
                returnMsg = resultMap.get("err_code_des");
            }
        }
        return ResultUtil.ERROR(returnMsg);
    }

    //==========================================================================================

    /**
     * 退款 调用证书
     * @param url 请求地址
     * @param data 请求数据
     * @param mchid 商户号
     * @return
     */
    private String sslCart(String url,String data,String mchid){
        StringBuffer message = new StringBuffer();
        try {
            log.info("请求数据 xml："+data);
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            //读取证书文件
            File file = ResourceUtils.getFile("classpath:apiclient_cert.p12");
            FileInputStream instream = new FileInputStream(file);
            keyStore.load(instream, mchid.toCharArray());
            // Trust own CA and all self-signed certs
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchid.toCharArray()).build();
            // Allow TLSv1 protocol only
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            HttpPost httpost = new HttpPost(url);
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            log.info("开始请求接口---"+httpost);
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                log.info("------请求结束----------结果："+entity);
                log.info(response.getStatusLine().toString());
                if (entity != null) {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                    String text;
                    while ((text = bufferedReader.readLine()) != null) {
                        message.append(text);
                    }
                }
                EntityUtils.consume(entity);
            } catch (IOException e) {
                log.error("sslCart()--error",e);
            } finally {
                response.close();
            }
        } catch (Exception e1) {
            log.error("sslCart()--error",e1);
        }
        return message.toString();
    }

    /**
     * 发送请求，以Post方式发送XML内容
     * @param urlStr 请求URL
     * @param xmlStr XML内容
     * @return 请求结果
     * @throws Exception
     */
    private String doPostXml(String urlStr , String xmlStr) throws Exception{
        String result;
        //创建连接
        URL urlClient = new URL(urlStr);
        HttpURLConnection connection = (HttpURLConnection) urlClient.openConnection();
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setRequestMethod("POST");
        connection.setUseCaches(false);
        connection.setInstanceFollowRedirects(true);
        connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
        connection.connect();

        //POST请求
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.write(xmlStr.getBytes());
        out.flush();
        out.close();

        //读取响应
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String lines;
        StringBuffer sb = new StringBuffer("");
        while ((lines = reader.readLine()) != null) {
            lines = new String(lines.getBytes(), "utf-8");
            sb.append(lines);
        }
        reader.close();
        // 断开连接
        connection.disconnect();
        result=sb.toString();
        return result;
    }

    /**
     * 解析微信字符串（XML）
     * @return Map<String, String>
     * @throws Exception
     */
    private Map<String, String> parseStringXml(String xml) throws Exception {
        Document document = DocumentHelper.parseText(xml);
        //得到xml根元素
        Element root = document.getRootElement();
        //得到根元素的所有子节点
        List<Element> elementList = root.elements();
        //将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<String, String>();
        //遍历所有子节点
        for(Element e : elementList){
            map.put(e.getName(), e.getText());
        }
        return map;
    }

    /**
     * 解析微信发来的请求（XML）
     *
     * @param request
     * @return
     * @throws Exception
     */
    private static TreeMap<String, String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        TreeMap<String, String> map = new TreeMap<>();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();

        // 遍历所有子节点
        elementList.forEach(e->{
            map.put(e.getName(), e.getText());
        });

        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }

    /**
     * 把商品数据包转成xml格式
     * @param packageParams 数据Map
     * @return xml格式的数据包
     */
    private String generateXmlPackage(TreeMap<String,String> packageParams){
        StringBuffer stringBuffer = new StringBuffer("<xml>");
        Set<String> setKey = packageParams.keySet();
        Iterator<String> it = setKey.iterator();
        while (it.hasNext()){
            String key = it.next();
            String value = packageParams.get(key);
            stringBuffer.append("<"+key+">"+value+"</"+key+">");
        }
        stringBuffer.append("</xml>");
        return stringBuffer.toString();
    }

    /**
     * 生成转账的参数Map
     * @param payConfigMap 支付数据配置
     * @param params 参数配置
     * @return TreeMap<String,String>
     */
    private TreeMap<String,String> createTransferParams(HttpServletRequest request,
                                                        TreeMap<String,String> payConfigMap,
                                                        Map<String,String> params){
        TreeMap<String,String> transferMap = new TreeMap<>();
        transferMap.put("mch_appid",payConfigMap.get("appid"));//公众号APPID
        transferMap.put("mchid",payConfigMap.get("mch_id"));//微信公众号对应商户号
        transferMap.put("nonce_str",StringUtils.getRandomString(32));//随机字符串，不长于32位
        transferMap.put("partner_trade_no", GenerateOrderNum.getInstance().generateOrderNo());//系统生成的订单号
        transferMap.put("openid", params.get("openId"));//微信号的openId
        //校验用户姓名选项
        //NO_CHECK：不校验真实姓名
        //FORCE_CHECK：强校验真实姓名
        transferMap.put("check_name", "NO_CHECK");//随机字符串，不长于32位
        Double totalFee = Double.parseDouble(params.get("amount"))*100D;
        transferMap.put("amount", String.valueOf(totalFee.intValue()));//企业付款金额，单位为分
        transferMap.put("desc", params.get("desc"));//企业付款备注
        transferMap.put("spbill_create_ip", WebUtils.getIpAddr(request));//Ip地址
        //获取签名
        String sign = createSign(transferMap,payConfigMap.get("payKey"));
        transferMap.put("sign",sign);
        return transferMap;
    }



    /**
     * 给微信发送的数据包
     * @param request HttpServletRequest
     * @param total_fee 支付金额（单位/分）
     * @param notifyUrl 回调路径
     * @param describe 商品描述
     * @param clientType 终端类型
     * @param openId 微信用户ID，微信内置浏览器支付时有用
     * @return TreeMap<String,String>
     */
    private TreeMap<String,String> createPackage(HttpServletRequest request,
                                                 String total_fee,
                                                 String notifyUrl,
                                                 String describe,
                                                 String clientType,
                                                 String openId,
                                                 Map<String,String> configMap) throws Exception{
        String outTradeNo = MD5.getMD5(UUID.randomUUID().toString()+System.currentTimeMillis()+StringUtils.getRandomString(32)).toUpperCase();
        TreeMap<String,String> treeMap = new TreeMap<>();
        if(clientType.equals(PayFormClient.ANDROID) || clientType.equals(PayFormClient.IOS)){//App支付
            //微信开放号appId
            treeMap.put("appid",configMap.get("openAppAppId"));
            //微信开放平台的商户号
            treeMap.put("mch_id",configMap.get("openMchId"));
        }else if (clientType.equals(PayFormClient.APPLY)){//小程序支付
            //小程序appi
            treeMap.put("appid",configMap.get("applyAppId"));
            //公众号的商户号
            treeMap.put("mch_id",configMap.get("mch_id"));
        }else if(clientType.equals(PayFormClient.PC) || clientType.equals(PayFormClient.H5)){//PC支付、H5支付
            //(公众号)微信支付分配的公众账号ID（企业号corpid即为此appId）
            treeMap.put("appid",configMap.get("appid"));
            //(公众号)微信支付分配的商户号
            treeMap.put("mch_id",configMap.get("mch_id"));
        }
        //随机字符串，长度要求在32位以内。
        treeMap.put("nonce_str", StringUtils.getRandomString(32));
        //商品简单描述，该字段请按照规范传递
        treeMap.put("body",describe);
        //商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一
        treeMap.put("out_trade_no",outTradeNo);
        //把价格转成分（整数，不能带小数）
        Double totalFee = Double.parseDouble(total_fee)*100D;
        //订单总金额，单位为分
        treeMap.put("total_fee",String.valueOf(totalFee.intValue()));
        //APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP。
        treeMap.put("spbill_create_ip", WebUtils.getIpAddr(request));
        //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
        treeMap.put("notify_url", notifyUrl);
        //PC扫码支付
        if(clientType.equals(PayFormClient.PC)){
            //取值如下：JSAPI公众号支付，NATIVE（扫码支付），APP等
            treeMap.put("trade_type","NATIVE");
        }
        //H5微信内容支付
        if(clientType.equals(PayFormClient.H5) || clientType.equals(PayFormClient.APPLY)){
            //如果是微信内部浏览器 ，或小程序支付
            if(DeviceUtils.isWeChat(request) || clientType.equals(PayFormClient.APPLY)){//微信客户端
                treeMap.put("trade_type","JSAPI");
                treeMap.put("openid",openId);
            }else{//手机浏览器支付
                treeMap.put("trade_type","MWEB");
            }
        }
        if(clientType.equals(PayFormClient.ANDROID) || clientType.equals(PayFormClient.IOS)){
            treeMap.put("trade_type","APP");
        }
        String payKey;
        if(clientType.equals(PayFormClient.ANDROID) || clientType.equals(PayFormClient.IOS)){//如果是App支付
            payKey= configMap.get("openPayKey");
        }else{//如果不是APP支付
            payKey= configMap.get("payKey");
        }
        //获取签名
        String sign = this.createSign(treeMap,payKey);
        treeMap.put("sign",sign);//通过签名算法计算得出的签名值，详见签名生成算法
        return treeMap;
    }

    /**
     * 获取微信签名
     * @param packageParams 参数包
     * @param key 支付Key
     * @return 加密后的签名
     */
    private String createSign(TreeMap<String, String> packageParams,String key) {
        StringBuffer sb = new StringBuffer();
        Set<Map.Entry<String,String>> es = packageParams.entrySet();
        Iterator<Map.Entry<String,String>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<String,String> entry = it.next();
            String k = entry.getKey();
            String v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k)&& !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key="+key);
        log.info("----------------------------md5 sb:" + sb);
        String sign = MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        log.info("--------------------------sign:"+sign);
        return sign;
    }

    /**
     * 验证支付签名
     * @param requestMap
     * @param payKey 支付密钥
     * @return true验证通过，false不通过
     */
    private boolean checkSignature(TreeMap<String, String> requestMap, String payKey) {
        TreeMap<String, String> signParams = new TreeMap<>();
        String signature = requestMap.get("sign");
        if (signature == null || signature.equals("")) {
            return false;
        }
        requestMap.remove("sign");
        for (java.util.Map.Entry<String, String> entry : requestMap.entrySet()) {// 循环放入树Map
            signParams.put(entry.getKey(), entry.getValue());
        }
        try {
            String sign = this.createSign(signParams, payKey);
            if (signature.equalsIgnoreCase(sign)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    /**
     * 微信支付回调后，向微信服务发xml信息
     * @param request
     * @param response
     * @param content
     * @throws IOException
     */
    public void sendXmlMessage(HttpServletRequest request, HttpServletResponse response, String content) throws IOException {
        log.info("--------------------------------------------" + content);
        String contentXml = "<xml><return_code><![CDATA[" + content + "]]></return_code></xml>";
        OutputStream os = response.getOutputStream();
        BufferedWriter resBr = new BufferedWriter(new OutputStreamWriter(os));
        resBr.write(contentXml);
        resBr.flush();
        resBr.close();
    }

    /**
     * 微信支付MD5加密编码
     * @param origin 原数据
     * @param charsetName 编码名（UTF-8）
     * @return 加密后的内容
     */
    private String MD5Encode(String origin, String charsetName) {
        String resultString  = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetName == null || "".equals(charsetName)){
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            }else{
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetName)));
            }
        } catch (Exception exception) {
        }
        return resultString;
    }

    /**
     * byte数组转换成16进度
     * @param b
     * @return
     */
    private String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    /**
     * byte转换成16进度字符
     * @param b
     * @return
     */
    private String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

}
